<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <script src="../react.development.js"></script>
    <script src="../react-dom.development.js"></script>
    <script src="../babel.js"></script>
  </head>
  <body>
    <div id="app"></div>
    <div id="app2"></div>
    <button id="Test">ON</button>
    <div id="app3"></div>
    <div id="app4"></div>
    <script type="text/babel">
      /*
        标签模板
          通常情况下，JavaScript 的 \ 具有特殊含义，代表转义
          比如 \n 表示换行.
          String.raw标签模板可以使得转义字符不进行转义。
          下面这个示例中 \n 并没有解释为换行 
    */
      console.log(String.raw`哈哈哈"\n"哈哈哈`);

      function tag(...args) {
        console.log(...args);
      }
      var a = 10;
      var b = "hello";
      // 标签写法
      tag`${a}=====${b}`;
      // -> 10 hello
      /*===================================================*/

      function puzzle() {
        return () => console.log(...arguments);
      }

      puzzle("a", "b", "c")(1, 2, 3);

      /*===================================================*/

      // React 事件处理
      /*
    React 元素的事件处理和 DOM 元素类似。但是有一点语法上的不同:

    React 事件绑定属性的命名采用驼峰式写法，而不是小写。
    如果采用 JSX 的语法你需要传入一个函数作为事件处理函数，而不是一个字符串(DOM 元素的写法) 

      HTML 通常写法是：

        <button onclick="activateLasers()">
          激活按钮
        </button>

      React 中写法为：

        <button onClick={activateLasers}>
          激活按钮
        </button>

      在 React 中另一个不同是你不能使用返回 false 的方式阻止默认行为， 你必须明确的使用 preventDefault。
      例如，通常我们在 HTML 中阻止链接默认打开一个新页面，可以这样写：

      <a href="#" onclick="console.log('点击链接'); return false">
        点我
      </a>

      在 React 的写法为：
      function ActionLink() {
        function handleClick(e) {
          e.preventDefault();
          console.log('链接被点击');
        }
      
        return (
          <a href="#" onClick={handleClick}>
            点我
          </a>
        );
      }

      实例中 e 是一个合成事件。

      使用 React 的时候通常你不需要使用 addEventListener 为一个已创建的 DOM 元素添加监听器。
      你仅仅需要在这个元素初始渲染的时候提供一个监听器。
    */
      class Toggle extends React.Component {
        /*
          TypeError:
             Super expression must either be null or a function, not undefined
          检查父类名称是否正确！
        */
        constructor(props) {
          super(props);
          this.state = {
            isToggleOn: true,
          };
          // 这边绑定是必要的，这样 `this` 才能在回调函数中使用
          this.handleClick = this.handleClick.bind(this);
        }

        handleClick() {
          this.setState((prevState) => ({
            isToggleOn: !prevState.isToggleOn,
          }));
        }

        render() {
          return (
            <button onClick={this.handleClick}>
              {this.state.isToggleOn ? "ON" : "OFF"}
            </button>
          );
        }
      }

      ReactDOM.render(<Toggle />, document.getElementById("app"));

      /*-------------------------------------------Demo------------------------------------------*/

      class Demo extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            isToggle: true,
          };
          this.onToggleClick = this.onToggleClick.bind(this);
        }

        //这里prve作为参数，名字是任意起的
        //不过，为了可读性和方便维护，这里最好叫 prevState
        onToggleClick() {
          this.setState((prve) => ({
            isToggle: !prve.isToggle,
          }));
        }

        render() {
          return (
            <button onClick={this.onToggleClick}>
              {this.state.isToggle ? "ON" : "OFF"}
            </button>
          );
        }
      }

      ReactDOM.render(<Demo />, document.getElementById("app2"));

      /**=================================Test==============================**/

      //原生实现切换 ON <--> OFF
      let toggle = true;
      document.getElementById("Test").onclick = function () {
        this.innerHTML = !toggle ? "ON" : "OFF";
        toggle = !toggle;
      };

      //**************************************************************************
      /*
        你必须谨慎对待 JSX 回调函数中的 this，类的方法默认是不会绑定 this 的。
        如果你忘记绑定 this.handleClick 并把它传入 onClick, 当你调用这个函数的时候 this 的值会是 undefined。

        这并不是 React 的特殊行为；它是函数如何在 JavaScript 中运行的一部分。
        通常情况下，如果你没有在方法后面添加 () ，例如 onClick={this.handleClick}，你应该为这个方法绑定 this。

        如果使用 bind 让你很烦，这里有两种方式可以解决。
        如果你正在使用实验性的属性初始化器语法，你可以使用属性初始化器来正确的绑定回调函数：
       */
      class LoggingButton extends React.Component {
        // 这个语法确保了 `this` 绑定在  handleClick 中
        // 这里只是一个测试
        handleClick = () => {
          console.log("this is:", this);
        };

        render() {
          return <button onClick={this.handleClick}>Click me</button>;
        }
      }

      ReactDOM.render(<LoggingButton />, document.getElementById("app3"));

      /********************************************************************************/
      /*如果你没有使用属性初始化器语法，你可以在回调函数中使用 箭头函数：*/
      class LoggingButton2 extends React.Component {
        handleClick() {
          console.log("this is:", this);
        }

        render() {
          //  这个语法确保了 `this` 绑定在  handleClick 中
          return (
            <button
              onClick={(e) => {
                this.handleClick(e), console.log(e);
              }}
            >
              Click me
            </button>
          );
        }
      }

      ReactDOM.render(<LoggingButton2 />, document.getElementById("app4"));

      /*
        使用这个语法有个问题就是每次 LoggingButton 渲染的时候都会创建一个不同的回调函数。
        在大多数情况下，这没有问题。然
        而如果这个回调函数作为一个属性值传入低阶组件，这些组件可能会进行额外的重新渲染。
        我们通常建议在构造函数中绑定或使用属性初始化器语法来避免这类性能问题。
      */
    </script>
  </body>
</html>
